﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RayDen.Library.Components.Color.Types;
using RayDen.Library.Components.Surface;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Data.Imaging;
using RayDen.RayEngine.Core.Types;
using RayDen.RayEngine.Data.Surface;

namespace RayDen.SmallRT.Components
{
    public abstract class Brdf
    {
        public abstract BsdfEvent BsdfType { get; }
        public abstract void Sample(ref Vector wi, ref Normal n, float u0, float u1, float lambda, out Vector dir, out float f);
    }

    public class Lambertian : Brdf
    {
        public override BsdfEvent BsdfType
        {
            get { return BsdfEvent.Diffuse; }
        }

        public override void Sample(ref Vector wi, ref Normal n, float u0, float u1, float lambda, out Vector dir, out float f)
        {
            float r1 = MathLab.M_2PI * u0, r2 = u1, r2s = MathLab.Sqrt(r2);
            var w = (Vector.Dot(ref n, ref wi) < 0 ? n : n * -1).ToVec();
            var u = ((Math.Abs(w.x) > 0.1f ? new Vector(0, 1, 0) : new Vector(1, 0, 0)) ^ w).Normalize();
            var v = w ^ u;
            dir = (u * MathLab.Cos(r1) * r2s + v * MathLab.Sin(r1) * r2s + w * MathLab.Sqrt(1 - r2)).Normalize();
            f = 1f;
        }
    }


    public class SpecularReflection : Brdf
    {

        public override BsdfEvent BsdfType
        {
            get { return BsdfEvent.Specular | BsdfEvent.Reflect; }
        }

        public override void Sample(ref Vector wi, ref Normal n, float u0, float u1, float lambda, out Vector dir, out float f)
        {
            var nl = n.ToVec();
            dir = Geometry.Reflect(ref wi, ref n);
                //new Vector(wi - nl * 2f * nl&wi);
            f = 1f;
        }
    }

    public class SpecularRefraction : Brdf
    {
        private IWavelengthDependentValue ior;

        public SpecularRefraction()
        {
            // ior = new ConstWavelengthDependentValue(1.5f);
            ior = ColorFactory.CreateRegular(SPD_Data.FromFile(@"F:\3D\spds\IndexOfRefraction\dense_flint_glass.txt").Select(i => (float)i).ToArray());
            //ior = ColorFactory.CreateRegular(SPD_Data.FromFile(@"F:\3D\spds\IndexOfRefraction\light_crown_glass.txt", SampledSpectrum.sampledLambdaStart).Select(i => (float)i).ToArray());



        }

        public override BsdfEvent BsdfType
        {
            get { return BsdfEvent.Specular | BsdfEvent.Transmit; }

        }

        public override void Sample(ref Vector wi, ref Normal n, float u0, float u1, float lambda, out Vector dir, out float f)
        {
            var nl = (Vector.Dot(ref n, ref wi) < 0 ? n : n * -1).ToVec();
            var n1 = n.ToVec();

            var reflRay = Geometry.Reflect(ref wi, ref n);

            bool into = (n1 & nl) > 0f;                // Ray from outside going in? 
            float nc = 1, nt = ior.Sample(lambda), nnt = into ? nc / nt : nt / nc, ddn = wi & nl, cos2t;
            if ((cos2t = 1f- nnt*nnt*(1f - ddn*ddn)) < 0) // Total internal reflection 
            {
                dir = reflRay;
                f = 1f;
                return;
            }
            var tdir = (wi * nnt - n1 * ((into ? 1 : -1) * (ddn * nnt + MathLab.Sqrt(cos2t)))).Normalize();
            var a = nt - nc;
            var b = nt + nc;
            var R0 = a * a / (b * b);
            var c = 1f - (into ? -ddn : tdir & n1);
            var Re = R0 + (1 - R0) * c * c * c * c * c;
            var Tr = 1f - Re;
            var P = .25f + .5f * Re;
            var RP = Re / P;
            var TP = Tr / (1f - P);
            if (u1 > u0)
            {
                if (u1 > P)
                {
                    dir = reflRay;
                    f = RP;
                }
                else
                {
                    dir = tdir;
                    f = TP;
                }
            }
            else
            {
                if ((u1 + u0) < 0.5)
                {
                    dir = reflRay;
                    f = Re;
                }
                else
                {
                    dir = tdir;
                    f = Tr;
                }
                
            }
        }
    }

    public class GlossyBrdf : Brdf
    {
        private float gloss = 30f;

        public override BsdfEvent BsdfType
        {
            get { return BsdfEvent.Glossy; }
        }

        public override void Sample(ref Vector wi, ref Normal n, float u0, float u1, float lambda, out Vector dir, out float f)
        {
            throw new NotImplementedException();
        }
    }
}
